package mspbots.qbd.online.core.app;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import mspbots.qbd.common.RedisKeys;
import mspbots.qbd.common.ResultSync;
import mspbots.qbd.online.core.AbstractToolsUtil;
import mspbots.qbd.online.core.BaseSyncService;
import mspbots.qbd.online.core.SyncRequest;
import mspbots.qbd.online.core.accounts.AccountServiceImpl;
import mspbots.qbd.online.oauth.AuthService;
import org.springframework.stereotype.Service;
import org.springframework.web.util.UriComponentsBuilder;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.time.Duration;
import java.util.Map;

/**
 * com.mspbots.sync.scheduled.connectwise.ConnectWiseSyncService
 *
 * @author Alex bob(https://github.com/vnobo)
 * @date Created by 2019/7/30
 */
@Log4j2
@Service
@RequiredArgsConstructor
public class OnlineServiceImpl extends AbstractToolsUtil {

    private final Map<String, BaseSyncService> syncServiceMap;
    private final AccountServiceImpl accountService;
    private final AuthService authService;

    public Mono<Boolean> onOff(Long tenantId, String onOff) {
        if ("ON".equals(onOff.toUpperCase())) {
            return this.stringRedisTemplate.opsForValue()
                    .set(RedisKeys.ONLINE_ON_OFF_TENANT_TASK_REDIS_KEY + tenantId, onOff.toUpperCase(),
                            Duration.ofDays(36500));
        } else {
            return this.stringRedisTemplate.opsForValue()
                    .delete(RedisKeys.ONLINE_ON_OFF_TENANT_TASK_REDIS_KEY + tenantId);
        }
    }

    public Mono<JsonNode> initialize(Long tenantId) {
        ResultSync resultSync = ResultSync.withDefault("TEST-SECURITY");
        return this.authService.getToken(tenantId)
                .flatMap(tokenState -> this.accountService.count().filter(count -> count > 0)
                        .subscriberContext(ctx -> ctx.put("tokenState", tokenState))
                        .subscriberContext(ctx -> ctx.put("resultSync", resultSync))
                        .doOnSuccess(result -> Flux.fromStream(this.syncServiceMap.values().parallelStream())
                                .flatMap(v -> Flux.concatDelayError(v.syncData(tokenState), v.syncProperties(tokenState))
                                ).subscribe())
                        .map(result -> this.objectMapper.createObjectNode().put("result", result)));
    }

    public Mono<Void> delete(Long tenantId) {
        return this.reactiveRedisTemplate.opsForValue()
                .delete(RedisKeys.ONLINE_AUTHORIZE_REDIS_KEY + tenantId)
                .then();
    }

    public Mono<ObjectNode> synchronise(Long tenantId, SyncRequest syncRequest) {
        return this.authService.getToken(tenantId)
                .doOnSuccess(tokenState -> Flux.fromStream(this.syncServiceMap.values().parallelStream())
                        .flatMap(v -> Flux.concatDelayError(v.syncData(tokenState), v.syncProperties(tokenState)))
                        .delayElements(Duration.ofSeconds(100))
                        .subscribe(result -> log.info("Synchronise manual sync success."),
                                err -> log.error("Synchronise manual sync error, msg: {}", err.getMessage())))
                .map(token -> this.objectMapper.createObjectNode().put("result", "success"));
    }

    public Mono<JsonNode> requestApi(Long tenantId, String href) {
        return super.onlineClient.get(UriComponentsBuilder.fromHttpUrl(href).build().toUri())
                .subscriberContext(ctx -> ctx.put("tenantId", tenantId));
    }
}
